import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

public class CouponCodeGenerator {
    private static final long SEQUENCE_MASK = 0x3FFL; // 000...011 1111 1111 (10 bits, but only using 10 for 0-999)  
    private static final AtomicLong lastTimestamp = new AtomicLong(0);
    private static final ConcurrentHashMap<Long, AtomicInteger> sequenceNumbers = new ConcurrentHashMap<>();
  
    public static synchronized String generateCouponCode() {  
        long timestamp = System.currentTimeMillis();  
  
        // 检查时间戳是否改变  
        if (timestamp < lastTimestamp.get()) {  
            throw new RuntimeException("Clock moved backwards. Refusing to generate id");  
        }  
  
        // 获取当前时间戳对应的序列号生成器  
        AtomicInteger sequenceNumber = sequenceNumbers.computeIfAbsent(timestamp, k -> new AtomicInteger(0));
  
        // 生成序列号并更新  
        int sequence = sequenceNumber.getAndIncrement();  
  
        // 检查序列号是否超过限制（实际上我们限制为999，但0x3FFL是1023，留出一些空间）  
        if (sequence > 999) {  
            // 如果超过限制，等待下一毫秒  
            // 注意：这里简单地使用while循环等待，但在生产环境中可能需要更复杂的策略  
            while (true) {  
                long currentTimestamp = System.currentTimeMillis();  
                if (currentTimestamp > timestamp) {  
                    // 时间戳改变，重置序列号并返回新的编号  
                    sequenceNumber = sequenceNumbers.put(currentTimestamp, new AtomicInteger(0));  
                    sequence = sequenceNumber.getAndIncrement();  
                    break;  
                }  
            }  
        }  
  
        // 组合时间戳和序列号  
        // 注意：我们实际上只需要13位，但这里我们仍然按照14位来格式化（如果时间戳后有空位，则前面补0）  
        long couponCode = (timestamp << 10) | (sequence & SEQUENCE_MASK);  
  
        // 转换为字符串（如果需要的话，可以截断或格式化）  
        // 但由于我们实际上只使用了13位，直接转换为字符串并截断到14位可能不是必需的  
        // 这里我们直接返回数字，或者你可以根据需求进行格式化  
        return String.format("%014d", couponCode).substring(0, 14); // 实际上这个substring调用是多余的，因为format已经保证了长度  
  
        // 更简洁的方式（如果不需要严格的14位字符串表示）  
        // return String.valueOf(couponCode);  
    }  
  
    public static void main(String[] args) throws InterruptedException {  
        for (int i = 0; i < 2000; i++) {  
            String couponCode = generateCouponCode();  
            System.out.println(couponCode);  
        }  
  
        // 注意：上面的循环在快速执行时可能会生成重复的编号，因为System.currentTimeMillis()的分辨率是毫秒  
        // 在实际应用中，你可能需要模拟并发或使用多线程来测试  
    }  
}